package MicroSpring.aopProxy.proxy.builder;

import MicroSpring.aopProxy.pjp.PjpImpl;
import MicroSpring.aopProxy.pjp.ProceedingJoinPoint;
import MicroSpring.aopProxy.proxy.ProxyFactory;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/*
    生成jdk代理对象
 */
public class ProxyBuilder<T> implements ProxyFactory {
    private final T targetBean;
    private final ClassLoader classLoader;
    private final Class[] interfaces;
    private final Set<String>targetMethodNames;
    private final Map<String,Object[]>targetArgsMap;
    private final Method adviceMethod;
    private final Object adviceBean;

    private final static int BEFORE=1;
    private final static int AFTER=2;
    private final static int AROUND=3;

    public ProxyBuilder(ClassLoader classLoader, Class[] interfaces,
                        Method adviceMethod,T targetBean,Object adviceBean) {
        this.classLoader = classLoader;
        this.interfaces = interfaces;
        this.targetMethodNames = new HashSet<>();
        this.targetArgsMap = new ConcurrentHashMap<>(256);
        this.adviceMethod=adviceMethod;
        this.targetBean=targetBean;
        this.adviceBean=adviceBean;
    }


    public ProxyBuilder addTargetMethodMap(String methodName){
        this.targetMethodNames.add(methodName);
        return this;
    }

    public ProxyBuilder addTargetArgs(String methodName,Object []args){
        this.targetArgsMap.put(methodName,args);
        return this;
    }


    /*
        得到一个前置增强的代理对象
     */
    public Object getBeforeProxyInstance() {
        return getProxyInstance(BEFORE);
    }

    /*
        得到一个后置增强的代理对象
    */
    public Object getAfterProxyInstance() {
        return getProxyInstance(AFTER);
    }




    public Object getAroundProxyInstance() {
        return getProxyInstance(AROUND);
    }

    public Object getProxyInstance(int type){
        InvocationHandler invocationHandler=null;
        switch (type){
            case BEFORE:
                invocationHandler= (proxy, method, args) -> {
                    if (targetMethodNames.contains(method.getName())){
                        adviceMethod.invoke(adviceBean,null);
                        return method.invoke(targetBean,args);
                    }else {
                        return method.invoke(targetBean,args);
                    }
                };
                break;
            case AFTER:
                invocationHandler= (proxy, method, args) -> {
                    if (targetMethodNames.contains(method.getName())){
                        Object o = method.invoke(targetBean, args);
                        adviceMethod.invoke(adviceBean,null);
                        return o;
                    }else {
                        return method.invoke(targetBean,args);
                    }
                };
                break;
            case AROUND:
                invocationHandler= (proxy, method, args) -> {
                    if (targetMethodNames.contains(method.getName())){
                        ProceedingJoinPoint pjp=new PjpImpl(method,targetBean,args);
                        return adviceMethod.invoke(adviceBean,pjp);
                    }else {
                        return method.invoke(targetBean,args);
                    }
                };
                break;
        }
        return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }

}
